#ifndef __UTILS_HELPER_HPP__
#define __UTILS_HELPER_HPP__

#include <thread>
#include <atomic>
#include <functional>
#include <mutex>
#include <vector>
#include <deque>
#include <queue>
#include <condition_variable>
#include <future>
#include <algorithm>
#include <chrono>
#include <iostream>

namespace sdk {
namespace utils {
namespace helper {

template <typename T>
    class threadsafe_queue
    {
    public:
        threadsafe_queue()
        {
        }

        void push(T &&data)
        {
            std::unique_lock<std::mutex> lk(mut);
            data_queue.push_back(std::move(data));
            data_cond.notify_one(); // 1
        }

        void push(T &data)
        {
            std::unique_lock<std::mutex> lk(mut);
            data_queue.push_back(std::move(data));
            data_cond.notify_one(); // 1
        }

        // 放在前面，用于优先获取，@晏崇高
        void push_front(T &&data)
        {
            std::unique_lock<std::mutex> lk(mut);
            data_queue.push_front(std::move(data));
            data_cond.notify_one(); // 1
        }

        void push_front(T &data)
        {
            std::unique_lock<std::mutex> lk(mut);
            data_queue.push_front(std::move(data));
            data_cond.notify_one(); // 1
        }

        void wait_and_pop(T &value) // 2
        {
            std::unique_lock<std::mutex> lk(mut);
            data_cond.wait(lk, [this]
                           { return !data_queue.empty(); });
            value = std::move(data_queue.front());
            data_queue.pop_front();
        }
        std::shared_ptr<T> wait_and_pop() // 3
        {
            std::unique_lock<std::mutex> lk(mut);
            data_cond.wait(lk, [this]
                           { return !data_queue.empty(); }); // 4
            std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
            data_queue.pop_front();
            return res;
        }
        bool try_pop(T &value)
        {
            std::unique_lock<std::mutex> lk(mut);
            if (data_queue.empty())
                return false;
            value = std::move(data_queue.front());
            data_queue.pop_front();
            return true;
        }
        bool try_pop(T &value, size_t &restSize)
        {
            std::unique_lock<std::mutex> lk(mut);
            if (data_queue.empty())
            {
                restSize = 0;
                return false;
            }
            value = std::move(data_queue.front());
            data_queue.pop_front();
            restSize = data_queue.size();
            return true;
        }

        std::shared_ptr<T> try_pop()
        {
            std::unique_lock<std::mutex> lk(mut);
            if (data_queue.empty())
                return std::shared_ptr<T>(); // 5
            std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
            data_queue.pop_front();
            return res;
        }
        bool empty() const
        {
            std::unique_lock<std::mutex> lk(mut);
            return data_queue.empty();
        }
        size_t size()const
        {
            std::unique_lock<std::mutex> lk(mut);
            return data_queue.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lk(mut);
            data_queue.clear();
        }

    private:
        mutable std::mutex mut;
        // @晏崇高 deque 替换 queue 结构，而不是使用优先级队列，因为优先级队列涉及到排序，而该方式减少了排序开销，也基本够用了
        std::deque<T> data_queue;
        std::condition_variable data_cond;
    };


template <class _T>
class SmartPtr
{
public:
    SmartPtr(const SmartPtr &) = delete;
    SmartPtr &operator=(const SmartPtr &) = delete;
    SmartPtr() = default;
    SmartPtr(_T *ptr, std::function<void(_T *)> freeFunc)
        : m_ptr(ptr), m_free(freeFunc)
    {
    }

    ~SmartPtr()
    {
        if (m_free && m_ptr)
        {
            m_free(m_ptr);
        }
    }

    SmartPtr(SmartPtr &&o)
    {
        m_free = std::move(o.m_free);
        m_ptr = std::move(o.m_ptr);
        o.m_ptr = nullptr;
    }

    SmartPtr &operator=(SmartPtr &&o)
    {
        m_free = std::move(o.m_free);
        m_ptr = std::move(o.m_ptr);
        o.m_ptr = nullptr;
        return *this;
    }

    _T *operator->() const
    {
        return get();
    }

    _T &operator*() const
    {
        return *m_ptr;
    }

    _T **operator&() const
    {
        return &m_ptr;
    }

    _T *get() const
    {
        return m_ptr;
    }

    operator _T *() const
    {
        return m_ptr;
    }

    operator bool() const
    {
        return m_ptr;
    }

private:
    _T *m_ptr = nullptr;
    std::function<void(_T *)> m_free = nullptr;
};


}
}
}
#endif

